# -*- coding: utf-8 -*-
# created on 2016/10/9

from sympy import sympify, Add
from mathsolver.functions.base import geometry
from mathsolver.functions.base import objects
from mathsolver.functions.base import sl_objects
from mathsolver.functions.base import fs_objects
from mathsolver.functions.base.base import BaseValue
import re
from mathsolver.functions.sympy_utils import get_all_child

trifunc = ["sin", "cos", "tan", "csc", "sec", "cot", "arctan", "arcsin", "arccos"]


def type_project(name, obj_type):
    if obj_type == "complex":
        if name == 'eq':
            return "ComplexEq"
        else:
            return name

    if name == "eq" and obj_type == "point":
        return name

    if name in ["variable", "pointname", "poly"] and obj_type == "ZhiXian":
        return "LineName"

    if name in ["variable", "pointname"] and obj_type in ["tuoyuan", "yuan.txt", "paowuxian", "shuangq", "dzshuangq",
                                                          "dzpaowuxian", "nzhixian", "graph"]:
        return "%sname" % obj_type

    if name in ["Variables", "PointNames"] and obj_type == "ZhiXian":
        return "LineNames"

    if name in ["variables", "pointnames"] and obj_type in ["tuoyuan", "yuan.txt", "paowuxian", "shuangq", "dzshuangq",
                                                            "dzpaowuxian", "nzhixian", "graph"]:
        return "%snames" % obj_type

    if "name" in name and "angle" not in obj_type:
        return name

    if name in ["line", "funceq", "variable"]:
        return name

    if name[-1] == "s":
        obj_type += "s"

    return obj_type


def _classify(name, expr, obj_name):
    if name == "poly":
        if expr == "x*O*y":
            pass
        elif re.search("[fghFGH]\\(.*\\)", expr.replace("log", "t").replace("_d", "").replace("_inverse", "")):
            name = "expression"

        elif "distance" in expr:
            name = "expression"

        elif "Angle" in expr and re.search("Angle\\([A-Za-z_0-9,]{3,10}\\)", expr):
            name = "expression"

        elif "vector" in expr:
            name = "expression"

        elif "_(" in expr:
            if "…" in expr:
                name = "SeqInfPoly"
            else:
                name = "SeqPoly"
        elif "…" in expr:
            expr = expr.replace("…", "...")
            name = "infpoly"

        elif "vector" in expr:
            name = "vectorpoly"
        elif "I" in expr:
            name = "ComplexPoly"
        elif "conjugate" in expr:
            name = "ComplexPoly"
        else:
            if re.search("sin|cos|tan|csc|sec|cot", expr):
                name = "SinPoly"
    elif name == "variable":
        name = "Variable"
        if "_(" in expr:
            name = "SeqVariable"
    # elif name == "variables":
    #     if "_(" in str(expr):
    #         name = "seqvariables"

    elif name == "func":
        if isinstance(expr, dict) and str(expr["var"]) != "x":
            if expr["type"] == "deri":
                expr = ["%s_d(%s)" % (expr["name"], expr["var"]), expr["expression"]]
                name = "funceq"
            else:
                name = "FuncAssign"
        else:
            if isinstance(expr, dict) and expr["expression"] and isinstance(expr["expression"], str):
                f = sympify(expr["expression"])
            elif isinstance(expr, list):
                f = sympify(expr[1])
            else:
                f = ""

            if f and get_all_child(f, lambda x: x.__class__.__name__ in trifunc and x.free_symbols):
                name = "SinFunc"
            elif isinstance(expr["expression"], list):
                name = "PieceFunc"

    elif name == "eq":
        if "distance" in ",".join(expr):
            name = "expression"

        elif re.search("Angle\\([A-Za-z_0-9,]{3,10}\\)", ",".join(expr)):
            name = "geoeq"

        elif "_(" in ",".join(expr):
            if "…" in ",".join(expr):
                name = "SeqEqInf"
            else:
                name = "SeqEq"

        elif "vector" in ",".join(expr):
            name = "vectoreq"

        elif "distance" in ",".join(expr):
            name = "diseq"

        elif "I" in ",".join(expr):
            # f = sympify(expr[0]) - sympify(expr[1])
            # if len(f.free_symbols.difference([sympify("z")])) > 0:
            #     name = "complexeq"
            name = "ComplexEq"
        elif "conjugate" in ",".join(expr):
            name = "ComplexEq"

        elif expr[0] == "y":
            obj = name2value(name, expr)
            args = obj.sympify()
            f = Add._from_args(args)
            if get_all_child(f, lambda x: x.__class__.__name__ in trifunc and x.free_symbols):
                name = "SinFunc"
        elif expr[0] != "y":
            obj = name2value(name, expr)
            args = obj.sympify()
            f = Add._from_args(args)
            if get_all_child(f, lambda x: x.__class__.__name__ in trifunc and x.free_symbols):
                name = "SinEq"

    elif name == "geoeq":
        if re.search("Angle\\([A-Za-z_0-9]{3,10}\\)", ",".join(expr)):
            name = "expression"

    elif name == "pointname":
        if expr == "R":
            name = "Inter"
            expr = ["(", "-∞", "∞", ")"]
        else:
            name = "PointName"
    elif name == "ineq":
        if "distance" in ",".join(expr):
            name = "expression"
        elif "_(" in ",".join(expr):
            name = "SeqIneq"
        elif "vector" in ",".join(expr):
            name = "expression"
    elif name == "funcname":
        name = "FuncName"
        var = sympify(expr["var"])
        if "f" in str(var) or "g" in str(var):
            name = "expression"
            expr = "%s(%s)" % (expr["name"], expr["var"])
    elif not isinstance(name, str) and issubclass(obj_name, objects.BaseFunc) and "name" not in name:
        if isinstance(expr, dict):
            var = sympify(expr["var"])
            if not var.free_symbols:
                name = "func_num"
            elif str(var) != "x":
                name = "func_expr"
        elif isinstance(expr, list):
            if expr[0] != "y":
                name = "eq"
    elif name == "complexeq":
        if len(expr[0]) != 1:
            name = "eq"
    elif name == "point":
        if "∞" in "".join(expr["value"]):
            name = "inter"
            expr = ["("] + expr["value"] + [")"]
    return name, expr


def classify(name, value):
    name = re.sub("[0-9]+", "", name).replace("andequal", "")
    if value["type"]:
        name = type_project(name, value["type"])
    expr = value["value"]
    obj_name = getobj(name)
    if name[-1] == "s" and name != "axis":
        values = [_classify(name[:-1], e, obj_name) for e in expr]
        names = [v[0] for v in values]
        if "SeqEq" in names:
            name = "SeqEq" + "s"
        elif "SeqIneq" in names:
            name = "SeqIneq" + "s"
        elif "SinEq" in names:
            name = "SinEq" + "s"
        elif "SinInq" in names:
            name = "SinInq" + "s"
        else:
            name = values[0][0] + "s"
        expr = [b for a, b in values]
    else:
        try:
            name, expr = _classify(name, expr, obj_name)
        except Exception:
            pass
    return name2value(name, expr)


def getobj(name):
    name = name.replace("andequal", "")
    if name == "vectoreqs":
        name = "VectorEqs"
    elif name == "vectoreq":
        name = "VectorEq"
    elif name == "geoeq":
        name = "GeoEq"
    elif name == "setop":
        name = "SetOp"
    elif name == "funceq":
        name = "FuncEq"
    elif name == "seteq":
        name = "SetEq"
    elif name == "funcname_d":
        name = "FuncNameD"
    elif name == "piecewisefunc":
        name = "PieceWiseFunc"
    elif name == "funcname_inverse":
        name = "FuncNameInverse"
    elif name == "sibian":
        name = "SiBian"
    name = "Base" + name[0].upper() + name[1:]
    if hasattr(geometry, name):
        return getattr(geometry, name)
    elif name[-1] == "s" and "Eqs" not in name and "Ineqs" not in name and "Axis" not in name:
        return name
    elif hasattr(sl_objects, name):
        return getattr(sl_objects, name)
    elif hasattr(fs_objects, name):
        return getattr(fs_objects, name)
    else:
        try:
            return getattr(objects, name)
        except Exception:
            pass


def name2value(name, value):
    if isinstance(value, BaseValue):
        return value
    obj = getobj(name)
    if isinstance(obj, str):
        obj = getattr(objects, "BaseMultiple")
        sub_obj = getobj(name[:-1])
        value = {"type": name[0].upper() + name[1:], "objs": [sub_obj(v) for v in value]}
    if isinstance(value, dict):
        return obj(value)
    else:
        try:
            return obj(value)
        except Exception:
            return obj(*value)


if __name__ == '__main__':
    pass
